package com.monochromebytes.mononet.virtual;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;

import com.monochromebytes.mononet.packet.Packet;

public abstract class DataPacket {

    public static final int      HEADER_LENGTH = ConnectionID.LENGTH + SequenceNumber.LENGTH
                                                       + Acknowledgment.LENGTH;

    public static final int      MAX_LENGTH    = Packet.MAX_LENGTH - DataPacket.HEADER_LENGTH;

    private final Packet         packet;
    private final SequenceNumber number;
    private final Acknowledgment acknowledgement;
    private final ConnectionID   connectionID;
    private final ByteBuffer     data;

    public DataPacket(Packet packet) {
        this.packet = packet;

        ByteBuffer packetBuffer = packet.getData();

        // Map the connection ID.
        packetBuffer.position(0);
        packetBuffer.limit(ConnectionID.LENGTH);
        this.connectionID = new ConnectionID(packetBuffer);

        // Map the packet sequence number.
        packetBuffer.position(ConnectionID.LENGTH);
        packetBuffer.limit(ConnectionID.LENGTH + SequenceNumber.LENGTH);
        this.number = new SequenceNumber(packetBuffer);

        // Map the acknowledment bitfield and sequence number.
        packetBuffer.position(ConnectionID.LENGTH + SequenceNumber.LENGTH);
        packetBuffer.limit(ConnectionID.LENGTH + SequenceNumber.LENGTH + Acknowledgment.LENGTH);
        this.acknowledgement = new Acknowledgment(packetBuffer);

        // Map the packet data.
        packetBuffer.position(ConnectionID.LENGTH + SequenceNumber.LENGTH + Acknowledgment.LENGTH);
        packetBuffer.limit(packetBuffer.capacity());
        this.data = packetBuffer.slice();
    }

    public ConnectionID getConnectionID() {
        return this.connectionID;
    }

    public SequenceNumber getSequenceNumber() {
        return this.number;
    }

    public Acknowledgment getAcknowledgment() {
        return this.acknowledgement;
    }

    public abstract VirtualAddress getConnectionAddress();

    public boolean isAcknowledged(DataPacket packet) {
        SequenceNumber number = packet.getSequenceNumber();
        return this.acknowledgement.isAcknowledged(number);
    }

    public ByteBuffer getData() {
        return this.data.duplicate();
    }

    public InetSocketAddress getReceipient() {
        return this.packet.getRecipient();
    }

    public InetSocketAddress getSender() {
        return this.packet.getSender();
    }

    public Packet getUDPPacket() {
        return this.packet;
    }

    public int getLength() {
        return this.packet.getLength();
    }

    public int getDataLength() {
        return this.data.remaining();
    }
}
